import logging
import os

from model.model import *


class ElecomModel(Model):
    makefile_commands = {
        'MODEL': '${MODEL_NAME}',
        'ROOT': '${MGM_PROJECT_BASE_DIR}',
        'DISPLAY_MODEL_NAME': '${DISPLAY_MODEL_NAME}',
        'BOOT_REPO': '$(MGM_PROJECT_BASE_DIR)/boot/$(BOOT_PACKAGE)',
        'OS_REPO': '$(MGM_PROJECT_BASE_DIR)/os/$(OS_PACKAGE)',
        'SDK_REPO': '$(MGM_PROJECT_BASE_DIR)/sdk/$(SDK_PACKAGE)',
        'CORE_REPO': '$(MGM_PROJECT_BASE_DIR)/core/code',
        'CUSTOMER_REPO': '${CUSTOMER_ABSOLUTE_DIR}',
        'ISS_SW_VERSION': '${ISS_SW_VERSION}',
        'OUTPUT_PREFIX': '${MODEL_NAME}-${ISS_SW_VERSION}',
        'OUTPUT_IMAGE_DIR': '${IMAGE_ABSOLUTE_DIR}/${OUTPUT_IMAGE_DIR}',
        'OUTPUT_IMAGE': '${OUTPUT_PREFIX}-IMAGE.img',
        'OUTPUT_BIN': '${OUTPUT_PREFIX}-PRECODE.bin',
        'OUTPUT_COMMON': '${PRECODE_BROADNAME}_${OUTPUT_PREFIX}.img',
        'OUTPUT_HEX': '${OUTPUT_PREFIX}-FW.hex',
        'OUTPUT_BOOT_HEX': '${OUTPUT_PREFIX}-BOOT.hex',
        'OUTPUT_OS_HEX': '${OUTPUT_PREFIX}-OS.hex',
        'OUTPUT_FS_HEX': '${OUTPUT_PREFIX}-FS.hex',
        'OUTPUT_ALL_HEX': '${OUTPUT_PREFIX}-ALL.hex',
        'OUTPUT_JFFS_HEX': '${OUTPUT_PREFIX}-JFFS2.hex',
        'OUTPUT_ISS_EXE_PATH': '${CORE_ABSOLUTE_DIR}/LR/ISS.exe'
    }

    version_re = re.compile(r'[ ]*RELEASE[ ]*VERSION[ ]*:[ ]*(\S+)')

    def __init__(self, **kwargs):
        super(ElecomModel, self).__init__(**kwargs)
        self._support_make_type = (make_all_clean_core_id, make_core_id, make_all_id, make_all_clean_all_id)
        self._make_dict.update({
            make_all_clean_all_id: self._make_all_clean_all,
        })
        self._checksum_file_name = 'checksum'
        self._ftp_var = {
            'ptr': '', 'sw': ''
        }
        self._ftp_last_release_max_search_cnt = 5
        self._fw_rar = ''
        self._all_rar = ''
        self._local_ptr_path = ''
        self._local_sw_path = ''
        self._project_name = 'elecom'
        self._release_note_tail_str = '_ReleaseNote.txt'

    def _make_all_clean_all(self) -> Popen:
        args = 'make all-clean all'
        return self._make(args)

    def update_all_repos(self):
        super(ElecomModel, self).update_all_repos()
        # elecom not update os, sdk, and boot reop
        # RGit(self._release_var['OS_REPO']).update()
        # RGit(self._release_var['SDK_REPO']).update()
        RGit(self._release_var['CORE_REPO']).update()
        RGit(self._release_var['CUSTOMER_REPO']).update()
        # RGit(self._release_var['BOOT_REPO']).update()
        io.emit(edm_model_info_report, f'Model {self.__str__()} core, customer repos updated.')

    def _ftp_ptr_path(self):
        return self._ftp_var['ptr']

    def _ftp_sw_path(self):
        return self._ftp_var['sw']

    def _sync_file_info(self):
        super(ElecomModel, self)._sync_file_info()
        """
        the local-remote map of release files

        :return: dict type file info
        """

        file_info = [{
            'name': self.__str__(),
            'children':
                [
                    {
                        'name': 'Hex files',
                        'children': [
                            {
                                'name': self._fw_rar,
                                'local': self._local_ptr_path,
                                'ftp': self._ftp_ptr_path(),
                            },
                            {
                                'name': self._all_rar,
                                'local': self._local_ptr_path,
                                'ftp': self._ftp_ptr_path(),
                            },
                            {
                                'name': self._fw_release_note_name(),
                                'local': self._local_ptr_path,
                                'ftp': self._ftp_ptr_path(),
                            },
                            {
                                'name': self._all_release_note_name(),
                                'local': self._local_ptr_path,
                                'ftp': self._ftp_ptr_path(),
                            },
                        ]
                    },
                    {
                        'name': 'Backup files',
                        'children': [
                            {
                                'name': self._release_var['OUTPUT_BIN'],
                                'local': self._local_sw_path,
                                'ftp': self._ftp_sw_path(),
                            },
                            {
                                'name': 'ISS.exe',
                                'local': self._local_sw_path,
                                'ftp': self._ftp_sw_path()
                            }
                        ]
                    }
                ]
        }]

        self._file_info = file_info

    def _sync_local_dirs(self):
        super(ElecomModel, self)._sync_local_dirs()
        model_release_to = combine_path([
            cfg.local_rls_store.__str__(),
            self.__str__(),
            self._iss_sw_ver().replace('-', '.'),
        ])
        local_ptr_path = Path(combine_path([
            model_release_to,
            'ptr'
        ]))
        local_sw_path = Path(combine_path([
            model_release_to,
            'sw'
        ]))
        self._local_ptr_path = local_ptr_path.__str__()
        self._local_sw_path = local_sw_path.__str__()

    def _ftp_model_dir(self, fw_dir: str, ver: str = None) -> str:
        """ TODO: may not be accurate """
        if ver is None:
            ver = self._version_in_ftp()
        return combine_path([
            fw_dir,
            ver,
            self.model_name()
        ])

    def _sync_ftp_dirs(self):
        super(ElecomModel, self)._sync_ftp_dirs()
        self._ftp_var['ptr'] = self._ftp_model_dir(cfg.release_ftp_fw_dir_list_elecom[0]) + '/ptr'
        self._ftp_var['sw'] = self._ftp_model_dir(cfg.release_ftp_fw_dir_list_elecom[0]) + '/sw'
        io.emit(edm_model_info_report,
                f'Model {self.__str__()} ftp path synchronized: {self._ftp_var["ptr"]}, {self._ftp_var["sw"]}')
        # TODO: Uncomment this for production env
        # !!!NOTE!!!: DO NOT use this method for get old release note, it may be removed in further version.
        # self._ftp_var['last_release_note'] = self._ftp_last_release_note_dir()

    def _version_in_ftp(self) -> str:
        return 'V' + self._iss_sw_ver().replace('-', '.')

    def _gen_release_note(self):
        self._gen_git_info()
        for r in self._last_release_note_arr:
            name = r['file']
            content = r['content']
            if len(content) > 0:
                head = []
                if '=====' in content[0]:
                    history = content[3:]
                else:
                    history = content[:]
                for i in range(len(content)):
                    m = self.version_re.match(content[i])
                    if m and m.group(1) == self._iss_sw_ver().replace('-', '.'):
                        cut = content[i + 1:]
                        for j in range(len(cut)):
                            n = self.version_re.match(cut[j])
                            if n:
                                history = cut[j - 1:]
                                break
            else:
                head = []
                history = []
            if 'ALL' in str(name):
                file_name = self._release_var['OUTPUT_ALL_HEX']
                local_release_note = combine_path([
                    self._local_ptr_path.__str__(),
                    self._all_release_note_name()
                ])
            else:
                file_name = self._release_var['OUTPUT_HEX']
                local_release_note = combine_path([
                    self._local_ptr_path.__str__(),
                    self._fw_release_note_name()
                ])

            release_note = head + self._release_note(file_name) + history
            with open(local_release_note, 'w') as f:
                f.writelines(release_note)

    def _fw_release_note_name(self) -> str:
        return self._model_name() + '_FW_' + self._version_in_ftp() + self._release_note_tail_str

    def _all_release_note_name(self) -> str:
        return self._model_name() + '_ALL_' + self._version_in_ftp() + self._release_note_tail_str

    def _release_to_local(self):
        super(ElecomModel, self)._release_to_local()

        Path(self._local_ptr_path).mkdir(parents=True, exist_ok=True)
        Path(self._local_sw_path).mkdir(parents=True, exist_ok=True)
        shutil.copy(self._fw(), self._local_ptr_path)
        shutil.copy(self._all(), self._local_ptr_path)
        shutil.copy(self._bin(), self._local_sw_path)
        shutil.copy(self._iss(), self._local_sw_path)
        # generate rar file for fw and all
        local_all_path = combine_path([
            self._local_ptr_path,
            self._release_var['OUTPUT_ALL_HEX']
        ])
        local_fw_path = combine_path([
            self._local_ptr_path,
            self._release_var['OUTPUT_HEX']
        ])
        logging.info(f'\r\n\r\n ====== start rar all hex {local_all_path} ... \r\n\r\n')
        all_rar = rar_file(local_all_path)
        logging.info(f'====== rar all hex {all_rar} done')
        logging.info(f'\r\n\r\n ====== start rar fw hex {local_fw_path} ... \r\n\r\n')
        fw_rar = rar_file(local_fw_path)
        logging.info(f'====== rar fw hex {fw_rar} done')
        self._all_rar = os.path.basename(all_rar)
        self._fw_rar = os.path.basename(fw_rar)
        # generate fw/all release notes and copy them to last_release_note directory
        self._gen_release_note()

        self._local_released_flag = True

    # def upload_all_to_ftp(self):
    #     super(ElecomModel, self).upload_all_to_ftp()
    #     ftp = RFtp(cfg.ftp_host)
    #     ftp.login(cfg.ftp_user, cfg.ftp_passwd)
    #
    #     for i in Path(self._local_ptr_path).iterdir():
    #         print(str(i).split('/')[-1])
    #         nm = str(i).split('/')[-1]
    #         try:
    #             ftp.upload_file(self._local_ptr_path, self._ftp_ptr_path(), nm)
    #         except Exception as e:
    #             print('%s -> %s upload failed.\n' % (self._local_ptr_path, nm))
    #             io.emit(edm_model_info_report, f'{self._local_ptr_path} -> {nm} upload failed. ({e})\n')
    #     for i in Path(self._local_sw_path).iterdir():
    #         print(str(i).split('/')[-1])
    #         nm = str(i).split('/')[-1]
    #         try:
    #             ftp.upload_file(self._local_sw_path, self._ftp_sw_path(), nm)
    #         except Exception as e:
    #             print('%s -> %s upload failed.\n' % (self._local_sw_path, nm))
    #             io.emit(edm_model_info_report, f'{self._local_sw_path} -> {nm} upload failed. ({e})\n')
    #
    #     ftp.close()

    def _fw(self):
        return combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self._release_var['OUTPUT_HEX']
        ])

    def _all(self):
        return combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self._release_var['OUTPUT_ALL_HEX']
        ])

    def _bin(self):
        return combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self._release_var['OUTPUT_BIN'],
        ])

    def _iss(self):
        return self._release_var['OUTPUT_ISS_EXE_PATH']

    def _iss_sw_ver(self) -> str:
        return self._release_var['ISS_SW_VERSION']

    def _hw_ver(self) -> str:
        return 'A1'

    def _mib_ver(self) -> str:
        return '1.00.009'

    def _boot_ver(self) -> str:
        return '1.00.002'

    def _img_dir(self) -> str:
        return self._release_var['OUTPUT_IMAGE_DIR']

    def _all_md5_checksum(self) -> str:
        local_all_path = combine_path([
            self._local_ptr_path,
            self._release_var['OUTPUT_ALL_HEX']
        ])
        c = do_bare(['md5sum', local_all_path])
        lines = str(c.stdout, 'utf-8').split('\n')
        return lines[0].split()[0]

    def _fw_md5_checksum(self) -> str:
        local_fw_path = combine_path([
            self._local_ptr_path,
            self._release_var['OUTPUT_HEX']
        ])
        c = do_bare(['md5sum', local_fw_path])
        lines = str(c.stdout, 'utf-8').split('\n')
        return lines[0].split()[0]

    def _boot_checksum(self) -> str:
        checksum_file = combine_path([self._img_dir(), 'checksum'])
        c = re.compile(r'\[([a-fA-F0-9]*)]')
        if Path(checksum_file).is_file():
            with open(checksum_file, 'r') as f:
                lines = f.readlines()
            for i in range(len(lines) - 1, -1, -1):
                if 'boot' in lines[i]:
                    m = c.match(lines[i])
                    if m:
                        return m.group(0).strip('[]')
        else:
            msg = f'{checksum_file} not exists\r\n'
            print(msg)
            io.emit(edm_model_info_report, msg)

        return ''

    def _fw_checksum(self) -> str:
        checksum_file = combine_path([self._img_dir(), self._checksum_file_name])
        if Path(checksum_file).exists():
            with open(checksum_file, 'r') as f:
                lines = f.readlines()
                for line in lines:
                    if 'squashfs' in line:
                        r = re.compile(r'\[([a-fA-F0-9]+)]')
                        groups = r.match(line)
                        if groups:
                            return groups[0]
        return ''

    def _release_note(self, file_name: str) -> []:
        if 'ALL' in file_name:
            md5_checksum = self._all_md5_checksum()
        else:
            md5_checksum = self._fw_md5_checksum()
        text = []
        today = datetime.date.today()
        text.append('===============================================================================\r\n')
        text.append('[PRODUCT NAME] : Elecom %s (FLASH: MX25L25635F)\r\n' % self.__str__())
        text.append('===============================================================================\r\n\r\n')
        text.append(
            '________________________________________________________________________________\r\n')
        text.append(
            '        RELEASE VERSION  : %s\r\n' % (
                self._iss_sw_ver().replace('-', '.')))
        text.append('        RELEASE DATE     : %04d/%02d/%02d\r\n' % (
            today.year, today.month, today.day))
        text.append(
            '        FILE NAME        : %s\r\n' % file_name)
        text.append(
            '        RELEASEE BY      : %s\r\n' % cfg.releaser)
        text.append(
            '________________________________________________________________________________\r\n\r\n')
        text.append('[CHECK-CODE]    : [N/A] \r\n')
        text.append('[MD5-CHECKSUM]  : [%s] \r\n' % md5_checksum)
        text.append('[FLASH-CHECKSUM]: [N/A]\r\n')
        text.append('[FLASH-CLEAR]   : [N/A]\r\n')
        text.append('[FW-CHECKSUM]   : %s\r\n' % self._fw_checksum())
        text.append('[BOOT-CHECKSUM] : [%s]\r\n' % self._boot_checksum())
        text.append('[PART-NO]       : [N/A]\r\n')
        text.append('[GIT]           :\r\n%s\r\n\r\n' % self._git_info)

        text.append('[HW-VER]        : %s\r\n' % self._hw_ver())
        text.append('[FW-VER]        : %s\r\n' % (self._iss_sw_ver().replace('-', '.')))
        text.append('[MIB-VER]       : %s\r\n' % self._mib_ver())
        text.append('[UTIL-VER]      : [N/A]\r\n')
        text.append('[BOOT-VER]      : %s\r\n' % self._boot_ver())
        text.append('[PLM-NO]        : [N/A]\r\n')
        text.append(
            '________________________________________________________________________________\r\n\r\n')
        text.append('Default: (information involved is depended by products)\r\n')
        text.append('.Default IP: 192.168.3.1\r\n')
        text.append('.Default User Login: admin/admin\r\n')
        text.append('.Default Administrator Login: admin/admin\r\n')
        text.append('.Default WAN Mode:\r\n')
        text.append('.Default DHCP Server:\r\n')
        text.append(
            '________________________________________________________________________________\r\n\r\n')
        text.append('New Feature:\r\n')
        text.append('-------------\r\n')
        text.append('%s\r\n\r\n' % (self._new_feature.replace('\n', '\r\n')))

        text.append('Bug Fixed:\r\n')
        text.append('-------------\r\n')
        text.append('%s\r\n\r\n' % (self._bug_fixed.replace('\n', '\r\n')))

        text.append('Changed:\r\n')
        text.append('-------------\r\n')
        text.append('%s\r\n\r\n' % (self._changed.replace('\n', '\r\n')))

        text.append('Note:\r\n')
        text.append('%s\r\n\r\n' % (self._note.replace('\n', '\r\n')))

        text.append('Known issues:\r\n')
        text.append('%s\r\n\r\n' % (self._known_issue.replace('\n', '\r\n')))
        text.append('-------------\r\n\r\n')

        return text
